package org.wikipedia.captcha; import android.app.Activity; import android.app.ProgressDialog; import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.Animatable; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.EditText; import android.widget.TextView; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.controller.BaseControllerListener; import com.facebook.drawee.view.SimpleDraweeView; import com.facebook.imagepipeline.image.ImageInfo; import org.mediawiki.api.json.RequestBuilder; import org.wikipedia.R; import org.wikipedia.WikipediaApp; import org.wikipedia.dataclient.WikiSite; import org.wikipedia.util.FeedbackUtil; import org.wikipedia.views.ViewAnimations; import retrofit2.Call; public class CaptchaHandler { private final Activity activity; private final View captchaContainer; private final View captchaProgress; private final SimpleDraweeView captchaImage; private final EditText captchaText; private final WikiSite wiki; private final View primaryView; private final String prevTitle; private ProgressDialog progressDialog; @Nullable private String token; @Nullable private CaptchaResult captchaResult; public CaptchaHandler(final Activity activity, final WikiSite wiki, final ProgressDialog progressDialog, final View primaryView, final String prevTitle, final String submitButtonText) { this.activity = activity; this.wiki = wiki; this.progressDialog = progressDialog; this.primaryView = primaryView; this.prevTitle = prevTitle; captchaContainer = activity.findViewById(R.id.captcha_container); captchaImage = (SimpleDraweeView) activity.findViewById(R.id.captcha_image); captchaText = (EditText) activity.findViewById(R.id.captcha_text); captchaProgress = activity.findViewById(R.id.captcha_image_progress); TextView submitButton = (TextView) activity.findViewById(R.id.captcha_submit_button); if (submitButtonText != null) { submitButton.setText(submitButtonText); submitButton.setVisibility(View.VISIBLE); } captchaImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { captchaProgress.setVisibility(View.VISIBLE); new CaptchaClient().request(wiki, new CaptchaClient.Callback() { @Override public void success(@NonNull Call<Captcha> call, @NonNull CaptchaResult result) { captchaResult = result; captchaProgress.setVisibility(View.GONE); handleCaptcha(true); } @Override public void failure(@NonNull Call<Captcha> call, @NonNull Throwable caught) { cancelCaptcha(); captchaProgress.setVisibility(View.GONE); FeedbackUtil.showError(activity, caught); } }); } }); } @Nullable public String token() { return token; } @Nullable public String captchaId() { if (captchaResult != null) { return captchaResult.getCaptchaId(); } return null; } @Nullable public String captchaWord() { if (captchaText != null) { return captchaText.getText().toString(); } return null; } public void restoreState(Bundle savedInstanceState) { if (savedInstanceState != null && savedInstanceState.containsKey("token") && savedInstanceState.containsKey("captcha")) { handleCaptcha(savedInstanceState.getString("token"), (CaptchaResult) savedInstanceState.getParcelable("captcha")); } } public void saveState(Bundle outState) { outState.putString("token", token); outState.putParcelable("captcha", captchaResult); } public boolean isActive() { return captchaResult != null; } public void handleCaptcha(@Nullable String token, @NonNull CaptchaResult captchaResult) { this.token = token; this.captchaResult = captchaResult; handleCaptcha(false); } private void handleCaptcha(boolean isReload) { if (captchaResult == null) { return; } if (!isReload) { ViewAnimations.crossFade(primaryView, captchaContainer); } captchaImage.setController(Fresco.newDraweeControllerBuilder() .setUri(captchaResult.getCaptchaUrl(wiki)) .setAutoPlayAnimations(true) .setControllerListener(new BaseControllerListener<ImageInfo>() { @Override public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) { ((AppCompatActivity)activity).getSupportActionBar().setTitle(R.string.title_captcha); if (progressDialog.isShowing()) { progressDialog.hide(); } // for our Dark theme, show a "negative image" of the captcha! final int maxColorVal = 255; if (WikipediaApp.getInstance().isCurrentThemeDark()) { float[] colorMatrixNegative = { -1.0f, 0, 0, 0, maxColorVal, //red 0, -1.0f, 0, 0, maxColorVal, //green 0, 0, -1.0f, 0, maxColorVal, //blue 0, 0, 0, 1.0f, 0 //alpha }; captchaImage.getDrawable().setColorFilter(new ColorMatrixColorFilter(colorMatrixNegative)); } else { captchaImage.getDrawable().clearColorFilter(); } // In case there was a captcha attempt before captchaText.setText(""); } }) .build()); } public void hideCaptcha() { ((AppCompatActivity) activity).getSupportActionBar().setTitle(prevTitle); ViewAnimations.crossFade(captchaContainer, primaryView); } public boolean cancelCaptcha() { if (captchaResult == null) { return false; } captchaResult = null; captchaText.setText(""); hideCaptcha(); return true; } public RequestBuilder populateBuilder(RequestBuilder builder) { if (captchaResult == null) { return builder; } return builder.param("captchaId", captchaResult.getCaptchaId()) .param("captchaWord", captchaText.getText().toString()); } }